﻿<%@ Page Culture="auto" UICulture="auto" Title="Service constraints" Language="C#"
    MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ServiceConstraint.aspx.cs"
    Inherits="BM.Tools.WebControls.MForm.Example.Constraints.ServiceConstraint" %>

<%@ Register TagPrefix="mf" Namespace="BM.Tools.WebControls.MForm.Controls" Assembly="BM.Tools.WebControls.MForm" %>
<%@ Register TagPrefix="mfadd" Namespace="BM.Tools.WebControls.MForm.Controls.Additions"
    Assembly="BM.Tools.WebControls.MForm" %>
<%@ Register TagPrefix="mfmod" Namespace="BM.Tools.WebControls.MForm.Controls.Modifiers"
    Assembly="BM.Tools.WebControls.MForm" %>
<%@ Register TagPrefix="mfvh" Namespace="BM.Tools.WebControls.MForm.Controls.ValueHandlers"
    Assembly="BM.Tools.WebControls.MForm" %>
<%@ Register TagPrefix="mftitle" Namespace="BM.Tools.WebControls.MForm.Controls.Titles"
    Assembly="BM.Tools.WebControls.MForm" %>
<asp:Content runat="server" ContentPlaceHolderID="Demonstration">
    <script type="text/javascript">
        function checkMonths(sender, args) {
            var result = new Object();
            result.IsValid = true;
            result.ErrorText = '';
            if (args.parameters.minMonth == args.parameters.maxMonth) {
                alert('Min and max months cannot be the same');
                result.ErrorText = 'Min and max months cannot be the same';
                result.IsValid = false;                
            } 
            
            return result;
        }
    </script>    
    <p>
        In the following demonstration, some imaginary yearly financial data is collected.
    </p>
    <p>
        The <code>YearlyIncome</code> needs to be specified, as well the month of the maximal
        and minimal incomes.
    </p>
    <p>
        In case of the <code>MaxMonthlyIncome</code> and <code>MinMonthlyIncome</code>,
        the month number and the income must be provided.
    </p>
    <p>
        For the data to be valid, these conditions need to be fulfilled:
    </p>
    <ul>
        <li>(1) The maximal monthly income must be lower than the minimal monthly income</li>
        <li>(2) The maximal monthly income must be higher than the average monthly income (yearly
            income / 12)</li>
        <li>(3) The minimal monthly income must be lower than the average monthly income (yearly
            income / 12)</li>
    </ul>
    <p>
        Those 3 conditions are checked using two <code>ServiceConstraint</code>s:</p>
    <ul>
        <li><code>CheckMaxMonthlyIncome</code> checks conditions (1) and (2) within the <code>
            MaxMonthlyIncome/Value</code> field</li>
        <li><code>CheckMinMonthlyIncome</code> checks conditions (1) and (3) within the <code>
            MinMonthlyIncome/Value</code> field</li>
    </ul>
    <mf:RootValidator runat="server" ControlToValidate="Root1" />
    <mf:Root ID="Root1" runat="server">
        <UriMappings>
            <mf:UriMapping Prefix="xs" Namespace="http://www.w3.org/2001/XMLSchema" />
            <mf:UriMapping Prefix="" Namespace="" />
        </UriMappings>
        <Contents>
            <mf:Branch Name="FinancialStatistics" runat="server">
                <Contents>
                    <mf:Leaf Name="YearlyIncome" runat="server" DataType="Decimal">
                    </mf:Leaf>
                    <mf:Branch Name="MaxMonthlyIncome" runat="server">
                        <Contents>
                            <mf:Leaf Name="Value" runat="server" DataType="Decimal">
                                <Additions>
                                    <mfadd:ServiceConstraint ServicePath="../AutoComplete/AutoComplete.asmx" ServiceMethod="CheckMaxMonthlyIncome"
                                        runat="server">
                                        <mfadd:ServiceParameter runat="server" Name="maxMonthlyIncome" Path="." />
                                        <mfadd:ServiceParameter runat="server" Name="minMonthlyIncome" Path="../../MinMonthlyIncome/Value" />
                                        <mfadd:ServiceParameter runat="server" Name="yearlyIncome" Path="../../YearlyIncome" />
                                    </mfadd:ServiceConstraint>
                                </Additions>
                            </mf:Leaf>
                            <mf:Leaf Name="Month" runat="server" DataType="PositiveInteger">
                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="MaxInclusive" Value="12" />
                                    <mfadd:ServiceConstraint runat="server" CustomScript="checkMonths">
                                        <mfadd:ServiceParameter runat="server" Name="maxMonth" Path="." /> 
                                        <mfadd:ServiceParameter runat="server" Name="minMonth" Path="../../MinMonthlyIncome/Month" /> 
                                    </mfadd:ServiceConstraint>
                                </Additions>                                
                            </mf:Leaf>
                        </Contents>
                    </mf:Branch>
                    <mf:Branch Name="MinMonthlyIncome" runat="server">
                        <Contents>
                            <mf:Leaf Name="Value" runat="server" DataType="Decimal">
                                <Additions>
                                    <mfadd:ServiceConstraint ServicePath="~/AutoComplete/AutoComplete.asmx" ServiceMethod="CheckMinMonthlyIncome"
                                        runat="server">
                                        <mfadd:ServiceParameter runat="server" Name="maxMonthlyIncome" Path="../../MaxMonthlyIncome/Value" />
                                        <mfadd:ServiceParameter runat="server" Name="minMonthlyIncome" Path="." />
                                        <mfadd:ServiceParameter runat="server" Name="yearlyIncome" Path="../../YearlyIncome" />
                                    </mfadd:ServiceConstraint>
                                </Additions>
                            </mf:Leaf>
                            <mf:Leaf Name="Month" runat="server" DataType="PositiveInteger">
                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="MaxInclusive" Value="12" />
                                </Additions>
                            </mf:Leaf>
                        </Contents>
                    </mf:Branch>
                </Contents>
            </mf:Branch>
        </Contents>        
    </mf:Root>
</asp:Content>
<asp:Content ID="Content1" ContentPlaceHolderID="Description" runat="server">
    <p>
        Sometimes it is desirable to check validity of the given filed on the server side.
    </p>
    <p>
        This is possible with the use of the <code>ServiceConstraint</code> control.
    </p>
    <p>
        The <code>ServiceConstraint</code> sends a web request to the server, with the defined
        parameters, and returns either true or false.
    </p>
    <p>
        To specify the address of the webservice, use the <code>ServicePath</code> property.
    </p>
    <p>
        To specify the name of the webmethod, use the <code>ServiceMethod</code> property.
    </p>
    <div class="Section">
        <mftitle:TitleControl runat="server" Text="The service parameter" />
        <p>
            The parameters that are sent to the webservice are defined as inner content properties
            with the use of the <code>ServiceParameter</code> control.
        </p>
        <p>
            The <code>ServiceParameter</code> control should be defined with the use of the
            following properties:
        </p>
        <ul>
            <li><code>Name</code> - the name of the variable, used in the webmethod</li>
            <li><code>Path</code> - the xpath-like path to the MForm item, whose value will be sent</li>
        </ul>
    </div>
    <div class="Section">
        <mftitle:TitleControl runat="server" Text="The webmethod" />
        <p>
            The server-side webmethod must contain the <code>[ScriptMethod]</code> attribute,
            and the class that this webmethod is within must contain the <code>[ScriptService]</code>
            attribute.
        </p>
        <p>
            The definition of this method must match the definition of the <code>ServiceConstraint</code>
            control:
        </p>
        <ul>
            <li>The name of the method must match the value of the <code>ServiceMethod</code> property</li>
            <li>The order of the <code>ServiceParameter</code> properties must match the order of
                method variables</li>
            <li>The name of the <code>ServiceParameter</code> property must match the name of the
                corresponding method variable</li>
            <li>The <code>ServiceParameter</code> value must be serializable with the use of its
                corresponding method's type</li>
        </ul>
        <dl class="Example">
            <dt>Example:</dt>
            <dd>
                If the definition of the <code>ServiceConstraint</code> looks like this:
                <div>
                    <pre>
&lt;mfadd:ServiceConstraint runat="server" ServicePath="<code>some/service/path</code>" ServiceMethod="<code>MethodName</code>" &gt;
            &lt;mfadd:ServiceParameter runat="server" Name="<code>someParameter</code>" Path="<code>some/parameter/path</code>" /&gt;
            &lt;mfadd:ServiceParameter runat="server" Name="<code>otherParameter</code>" Path="<code>other/parameter/path</code>" /&gt;
&lt;/mfadd:ServiceConstraint&gt;
                </pre>
                </div>
                then the method's definition should look like this:
                <div>
                    <pre>
        <code>[ScriptMethod]</code>
        public ServiceConstraintResponse <code>MethodName</code>(<i>someType</i> <code>someParameter</code>, <i>otherType</i> <code>otherParameter</code>) {
            ...
        }
                        </pre>
                </div>
            </dd>
        </dl>
        <div class="Remark">
            It is also worth mentioning that the client side may return null to the server side even for .NET non-nullable types (like int, decimal or DateTime). 
            To properly handle such a situation, a nullable versions of these types should be used as method parameters (int?, decimal?, DateTime? accordingly).
        </div>
    </div>
    <div class="Section">
        <mftitle:TitleControl runat="server" Text="The CustomScript property" />
        <p>
            The ServiceConstraint also provides a functionality to invoke a client-side method instead of a server-side service.
            This can be done by specifying the <code>CustomScript</code> property.
        </p>
        <p>
            When the <code>CustomScript</code> property is set, it takes precedence over the <code>ServicePath</code> and <code>ServiceMethod</code> settings, which in such a situation are not used.
            The <code>CustomScript</code> should be a method, that takes two parameters:
        </p>
        <ul>
            <li>sender - the client-side ServiceConstraint object;</li>
            <li>args- the event args, which contain <code>parameters</code> property with a hashtable of all the parameters;</li>
        </ul>
        <p>
            The method should return an object with two properties set:
        </p>
        <ul>
            <li>IsValid - an information whether the validation was successful</li>
            <li>ErrorText - the error text that should be displayed</li>
        </ul>
    </div>
</asp:Content>
